#include "comm/my_str.h"
#include "comm/my_inet.h"
#include "comm/my_cleanup.h"
#include "comm/my_hooks.h"
#include "event/my_conn.h"
#include "event/my_worker.h"
#include "event/my_event_loop.h"
#include "config/my_cache_conf.h"

static my_bool_t use_et_model = MY_FALSE;

void my_test_server_read_handler(my_event_t *event)
{
    my_conn_t *conn = event->data;

    my_syscall_result_t res;
    do {
        char data[1024];
        res = my_conn_read(conn, data, sizeof(data) - 1);
        if (res.res > 0) {
            data[(int)res.res] = '\0';
            MY_LOG_DEBUG("test_server read data: %s", data);
        }
    } while(res.res > 0);

    if (res.res < 0 && res.eno == EAGAIN) {
        event->ready = 0;
        MY_LOG_DEBUG("test_server read eagain");
        return;
    }

    if (res.res < 0) {
        MY_LOG_ERROR("test_server read errno: %d", res.eno);
    }

    if (res.res == 0) {
        MY_LOG_DEBUG("test_server conn closed");
    }

    my_event_loop_remove_conn(conn);
    my_conn_close(conn);
    my_thread_free(conn);
}

void my_test_server_timer_handler(my_event_t *event)
{
    my_conn_t *conn = event->data;
    if (!event->timedout) {
        MY_LOG_DEBUG("test_server not timer task callback");
        return;
    }

    event->timedout = 0;
    event->handler = my_test_server_read_handler;

    // 接口幂等性
    my_event_loop_enabled_io(conn, MY_FALSE);
    my_event_loop_enabled_io(conn, MY_FALSE);
    my_event_loop_disabled_io(conn, MY_FALSE);
    my_event_loop_disabled_io(conn, MY_FALSE);
    my_event_loop_enabled_io(conn, MY_FALSE);
    my_event_loop_disabled_io(conn, MY_FALSE);
    my_event_loop_enabled_io(conn, MY_FALSE);
}

void my_test_server_handler(my_dispatch_event_t *dispatch_event)
{
    int accept_fd = (int)(intptr_t)dispatch_event->data;
    my_thread_free(dispatch_event);

    my_conn_t *server_conn = my_thread_calloc(sizeof(my_conn_t));
    my_conn_init(server_conn, use_et_model ? MY_CONN_EPOLL_ET : MY_CONN_EPOLL_LT);
    server_conn->fd = accept_fd;

    my_event_loop_start_io(server_conn);

    server_conn->read.handler = my_test_server_timer_handler;

    // 接口幂等性
    my_event_loop_enabled_io(server_conn, MY_FALSE);
    my_event_loop_enabled_io(server_conn, MY_FALSE);
    my_event_loop_disabled_io(server_conn, MY_FALSE);
    my_event_loop_disabled_io(server_conn, MY_FALSE);
    my_event_loop_enabled_io(server_conn, MY_FALSE);
    my_event_loop_disabled_io(server_conn, MY_FALSE);
    my_event_loop_enabled_io(server_conn, MY_FALSE);
    my_event_loop_disabled_io(server_conn, MY_FALSE);

    my_event_loop_add_timer(&server_conn->read, 1000);
}


const char *msg = "abcdefghi\n";

void my_test_client_timer_handler(my_event_t *event)
{
    my_conn_t *conn = event->data;

    int step = 0;
    if (conn->data != NULL) {
        step = (int)(intptr_t)conn->data;
    }

    step++;
    conn->data = (void *)(intptr_t)step;

    if (step > 5) {
        my_event_loop_remove_conn(conn);
        my_conn_close(conn);
        my_thread_free(conn);
        return;
    }

    my_syscall_result_t res;
    res = my_conn_write(conn, (void *)msg, (int64_t)strlen(msg));
    if (res.res != (int64_t)strlen(msg)) {
        MY_LOG_FATAL("send failed, except %d, but got %d, errno: %d", strlen(msg), res.res, res.eno);
    }

    my_event_loop_add_timer(&conn->read, 100);
}

void my_test_client_handler(my_dispatch_event_t *dispatch_event)
{
    int conn_fd = (int)(intptr_t)dispatch_event->data;
    my_thread_free(dispatch_event);

    my_conn_t *client_conn = my_thread_calloc(sizeof(my_conn_t));
    my_conn_init(client_conn, use_et_model ? MY_CONN_EPOLL_ET : MY_CONN_EPOLL_LT);
    client_conn->fd = conn_fd;

    my_syscall_result_t res;
    res = my_conn_write(client_conn, (void *)msg, (int64_t)strlen(msg));
    if (res.res != (int64_t)strlen(msg)) {
        MY_LOG_FATAL("send failed, except %d, but got %d, errno: %d", strlen(msg), res.res, res.eno);
    }

    client_conn->read.handler = my_test_client_timer_handler;
    my_event_loop_add_timer(&client_conn->read, 100);
}

void test_conn()
{
    my_syscall_result_t res;

    // socket bind listen
    res = my_syscall_socket(AF_INET, SOCK_STREAM, 0);
    if (res.eno != 0) {
        MY_LOG_FATAL("socket() failed, errno: %d", res.eno);
    }

    int listen_fd = (int)res.res;

    int reuseaddr_val = 1;
    res = my_syscall_setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_val, sizeof(int));
    if (res.eno != 0) {
        MY_LOG_FATAL("setsockopt(SO_REUSEADDR) failed, errno: %d", res.eno);
    }

    my_address_t address;
    if (!my_address_parser("127.0.0.1:9999", &address, NULL)) {
        MY_LOG_FATAL("parser address failed");
    }

    socklen_t sockaddr_len = sizeof(my_sockaddr_in_t);
    res = my_syscall_bind(listen_fd, &address.sockaddr, sockaddr_len);
    if (res.eno != 0) {
        MY_LOG_FATAL("bind() failed, errno: %d", res.eno);
    }

    res = my_syscall_listen(listen_fd, 64);
    if (res.eno != 0) {
        MY_LOG_FATAL("listen() failed, errno: %d", res.eno);
    }

    // socket connect
    res = my_syscall_socket(AF_INET, SOCK_STREAM, 0);
    if (res.eno != 0) {
        MY_LOG_FATAL("socket() failed, errno: %d", res.eno);
    }

    int conn_fd = (int)res.res;
    res = my_syscall_connect(conn_fd, &address.sockaddr, sockaddr_len);
    if (res.eno != 0) {
        MY_LOG_FATAL("connect() failed, errno: %d", res.eno);
    }

    res = my_syscall_accept4(listen_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
    if (res.eno != 0) {
        MY_LOG_FATAL("accept() failed, errno: %d", res.eno);
    }

    int accept_fd = (int)res.res;

    my_dispatch_event_t *server_dispatch_event = my_thread_calloc(sizeof(my_dispatch_event_t));
    server_dispatch_event->handler = my_test_server_handler;
    server_dispatch_event->data = (void *)(intptr_t)accept_fd;

    my_dispatch_event_t *client_dispatch_event = my_thread_calloc(sizeof(my_dispatch_event_t));
    client_dispatch_event->handler = my_test_client_handler;
    client_dispatch_event->data = (void *)(intptr_t)conn_fd;

    my_worker_processor_post_event(client_dispatch_event);
    my_worker_processor_post_event(server_dispatch_event);
}

int main(int argc, char **argv) 
{
    if (argc > 1 && strcmp(argv[1], "et") == 0) {
        use_et_model = MY_TRUE;
    }

    if (use_et_model) {
        MY_LOG_DEBUG("use ET model");
    } else {
        MY_LOG_DEBUG("use LT model");
    }

    my_hooks_init();

    my_cache_conf_init();

    my_worker_processor_start(1024, 1, 0);

    test_conn();

    my_worker_processor_stop();

    my_cleanup_list_exec(&g_cleanup_list);
    my_mem_assert_leak();

    MY_LOG_DEBUG("test pass");

    return 0;
}